//------------------------------------------------------------------------------
// <copyright file="RpcArchiveImpl.cpp" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

#include "stdafx.h"

#include "RpcServerImpl.h"
#include "RpcArchiveImpl.h"
#include "RpcBinaryDataImpl.h"
#include <stdlib.h>

#ifndef _DURANGO
#include <strsafe.h>
#endif

using namespace Microsoft::Internal::GamesTest::Rpc::Server;

const wchar_t ContainerTypeMetaKey[] = L"__type";
const wchar_t FieldTypeMetaKey[] = L"__ftype";

RpcArchiveImpl::RpcArchiveImpl()
{
	// Add __type field immediately to ensure it's the first one in serialized JSON, otherwise
	// the RPC client will receive "type does not support IConvertible" exception from JSON contract serializer.
	AddTypeMetadataIfMissing();
}

RpcArchiveImpl::RpcArchiveImpl(const RpcArchiveImpl& other)
{
	if(!other.m_jsonObject.Empty())
	{
		// Deep-copy the underlying JSON object.
		m_jsonObject = other.m_jsonObject;

		// Copy the meta-data directly (strings in the map are never modified, only replaced)
		m_fieldTypes = other.m_fieldTypes;
	}
}

RpcArchiveImpl::RpcArchiveImpl(RpcArchiveImpl&& other)
{
	// Transfer ownership of the other archive's JSON object
	m_jsonObject = std::move(other.m_jsonObject);

	// Transfer ownership of the other archive's meta-data
	m_fieldTypes = other.m_fieldTypes;
	other.m_fieldTypes.clear();
}

RpcArchiveImpl::RpcArchiveImpl(const JsonNode& jsonObject)
{
	Deserialize(jsonObject);
}

RpcArchiveImpl& RpcArchiveImpl::operator=(const RpcArchiveImpl& other)
{
	if(this == &other)
	{
		return *this;
	}

	if(!other.m_jsonObject.Empty())
	{
		// Deep-copy the underlying JSON object to avoid having same references
		m_jsonObject = other.m_jsonObject;

		// Copy the meta-data directly (strings in the map are never modified, only replaced)
		m_fieldTypes = other.m_fieldTypes;
	}
	else
	{
		// Clear the archive and meta-data
		Clear();
	}

	return *this;
}

RpcArchiveImpl& RpcArchiveImpl::operator=(RpcArchiveImpl&& other)
{
	if(this == &other)
	{
		return *this;
	}

	// Transfer ownership of the JSON object
	m_jsonObject = std::move(other.m_jsonObject);

	// Transfer ownership of the meta-data
	m_fieldTypes = other.m_fieldTypes;
	other.m_fieldTypes.clear();

	return *this;
}

bool RpcArchiveImpl::HasNamedValue(_In_z_ const wchar_t* fieldName) const
{
	if(fieldName == NULL || m_jsonObject.Empty())
	{
		return false;
	}

	return m_jsonObject.Exists(fieldName);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ RpcArchiveImpl* fieldValue) const
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	try
	{
		*fieldValue = RpcArchiveImpl(m_jsonObject.GetValue(fieldName));
	}
	catch (std::out_of_range)
	{
		hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		goto end;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ bool* fieldValue) const
{ 
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	*fieldValue = false;

	try
	{
		*fieldValue = m_jsonObject.GetBooleanValue(fieldName);
	}
	catch (std::out_of_range)
	{
		hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		goto end;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, RpcArchiveImpl& fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	if (!fieldValue.GetJson().Empty())
	{
		// Set field value
		m_jsonObject.SetValue(fieldName, fieldValue.GetJson());

		// Write the meta-data
		m_fieldTypes[fieldName] = FieldTypes::Archive;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, bool fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	// Set field value
	m_jsonObject.SetValue(fieldName, fieldValue);

	// Write the meta-data
	m_fieldTypes[fieldName] = FieldTypes::Bool;

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ unsigned char* fieldValue) const
{ 
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	CHK(GetNumericField<unsigned char>(fieldName, fieldValue));

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, unsigned char fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	SetNumericField<unsigned char>(fieldName, fieldValue, FieldTypes::UInt8);

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ char* fieldValue) const
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	CHK(GetNumericField<char>(fieldName, fieldValue));

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, char fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	SetNumericField<char>(fieldName, fieldValue, FieldTypes::Char);

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ signed char* fieldValue) const
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	CHK(GetNumericField<signed char>(fieldName, fieldValue));

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, signed char fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	SetNumericField<signed char>(fieldName, fieldValue, FieldTypes::Int8);

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ unsigned short* fieldValue) const
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	CHK(GetNumericField<unsigned short>(fieldName, fieldValue));

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, unsigned short fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	SetNumericField<unsigned short>(fieldName, fieldValue, FieldTypes::UShort);

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ short* fieldValue) const
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	CHK(GetNumericField<short>(fieldName, fieldValue));

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, short fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	SetNumericField<short>(fieldName, fieldValue, FieldTypes::Short);

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ int* fieldValue) const
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	CHK(GetNumericField<int>(fieldName, fieldValue));

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, int fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	SetNumericField<int>(fieldName, fieldValue, FieldTypes::Int);

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ unsigned int* fieldValue) const
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	CHK(GetNumericField<unsigned int>(fieldName, fieldValue));

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, unsigned int fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	SetNumericField<unsigned int>(fieldName, fieldValue, FieldTypes::UInt);

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ long* fieldValue) const
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	CHK(GetNumericField<long>(fieldName, fieldValue));

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, long fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	SetNumericField<long>(fieldName, fieldValue, FieldTypes::Long);

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ unsigned long* fieldValue) const
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	CHK(GetNumericField<unsigned long>(fieldName, fieldValue));

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, unsigned long fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	SetNumericField<unsigned long>(fieldName, fieldValue, FieldTypes::ULong);

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ long long* fieldValue) const
{
	// http://en.wikipedia.org/wiki/JavaScript_syntax#Number
	// long long and unsigned long long values are encoded as strings because all numbers in JSON
	// are represented in binary as IEEE-754 Doubles, which provides an accuracy nearly 16 significant digits.
	// If the number becomes longer than 16 digits, then it can start to lose accuracy.

	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	*fieldValue = 0LL;

	try
	{
		JsonString string = m_jsonObject.GetStringValue(fieldName);

		if (!string.Empty())
		{
			*fieldValue = _wcstoi64(string, nullptr, 10);
		}
	}
	catch (std::out_of_range)
	{
		hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		goto end;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, long long fieldValue)
{
	// http://en.wikipedia.org/wiki/JavaScript_syntax#Number
	// long long and unsigned long long values are encoded as strings because all numbers in JSON
	// are represented in binary as IEEE-754 Doubles, which provides an accuracy nearly 16 significant digits.
	// If the number becomes longer than 16 digits, then it can start to lose accuracy.
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	// The longest value of an long long is -9223372036854775808, which is 20 characters.
	// The buffer here is rounded up to the nearest power of 2.
	const int BUFFER_SIZE = 32;
	wchar_t buf[BUFFER_SIZE];
	_i64tow_s(fieldValue, buf, BUFFER_SIZE, 10);

	// Write the field value
	m_jsonObject.SetValue(fieldName, buf);

	// Write the meta-data
	m_fieldTypes[fieldName] = FieldTypes::Int64;

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ unsigned long long* fieldValue) const
{
	// http://en.wikipedia.org/wiki/JavaScript_syntax#Number
	// long long and unsigned long long values are encoded as strings because all numbers in JSON
	// are represented in binary as IEEE-754 Doubles, which provides an accuracy nearly 16 significant digits.
	// If the number becomes longer than 16 digits, then it can start to lose accuracy.

	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	*fieldValue = 0ULL;

	try
	{
		JsonString string = m_jsonObject.GetStringValue(fieldName);

		if (!string.Empty())
		{
			*fieldValue = _wcstoui64(string, nullptr, 10);
		}
	}
	catch (std::out_of_range)
	{
		hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		goto end;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, unsigned long long fieldValue)
{
	// http://en.wikipedia.org/wiki/JavaScript_syntax#Number
	// long long and unsigned long long values are encoded as strings because all numbers in JSON
	// are represented in binary as IEEE-754 Doubles, which provides an accuracy nearly 16 significant digits.
	// If the number becomes longer than 16 digits, then it can start to lose accuracy.

	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	// The longest value of an long long is 18446744073709551616, which is 20 characters.
	// The buffer here is rounded up to the nearest power of 2.
	const int BUFFER_SIZE = 32;
	wchar_t buf[BUFFER_SIZE];
	_ui64tow_s(fieldValue, buf, BUFFER_SIZE, 10);

	// Write the field value
	m_jsonObject.SetValue(fieldName, buf);

	// Write the meta-data
	m_fieldTypes[fieldName] = FieldTypes::UInt64;

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ float* fieldValue) const
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	CHK(GetNumericField<float>(fieldName, fieldValue));

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, float fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	SetNumericField<float>(fieldName, fieldValue, FieldTypes::Float);

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ double* fieldValue) const
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldValue);
	VERIFY_PARAM_NOT_NULL(fieldName);

	CHK(GetNumericField<double>(fieldName, fieldValue));

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, double fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	SetNumericField<double>(fieldName, fieldValue, FieldTypes::Double);

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedValue(_In_z_ const wchar_t* fieldName, _Out_ RpcBinaryDataImpl* fieldValue) const
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	try
	{
		*fieldValue = RpcBinaryDataImpl(m_jsonObject.GetValue(fieldName));
	}
	catch (std::out_of_range)
	{
		hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		goto end;
	}

end:
	return hr;
}


GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedValue(_In_z_ const wchar_t* fieldName, RpcBinaryDataImpl& fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);

	// Write the key and data	
	m_jsonObject.SetValue(fieldName, fieldValue.GetJson());

	// Write the meta data
	m_fieldTypes[fieldName] = FieldTypes::Blob;

end:
	return hr;
}

const JsonNode& RpcArchiveImpl::GetJson() const
{
	// Serialize member meta-data into a hidden array inside above JSON object.
	const_cast<RpcArchiveImpl*>(this)->SerializeTypeMetadata();
	
	return m_jsonObject;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::MeasureNamedStringValue(_In_z_ const wchar_t* fieldName, _Out_ unsigned long* stringLength) const
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(stringLength);

	*stringLength = 0;

	try
	{
		JsonString string = m_jsonObject.GetStringValue(fieldName);

		if (string.Empty())
		{
			// Since the string is empty then we'll return 1 to indicate space for the null-termination character.
			*stringLength = 1;
		}
		else
		{
			*stringLength = string.GetLength() + 1;
		}
	}
	catch (std::out_of_range)
	{
		hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		goto end;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedStringValue(_In_z_ const wchar_t* fieldName, _Out_writes_z_(bufferLength) wchar_t* buffer, _In_  unsigned long bufferLength, _Out_opt_ unsigned long* charactersWritten) const
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(buffer);

	if (charactersWritten != nullptr)
	{
		*charactersWritten = 0;
	}

	try
	{
		// Get string value
		JsonString string = m_jsonObject.GetStringValue(fieldName);
		unsigned long length = string.GetLength() + 1;

		// Verify it can fit in provided buffer completely
		if (bufferLength < length)
		{
			hr = GAMESTEST_RPC_E_DESTINATION_BUFFER_TOO_SMALL;
			goto end;
		}

		if (!string.Empty())
		{
			// Copy string into provided buffer
			wcsncpy_s(buffer, bufferLength, string, bufferLength);

			if (charactersWritten != nullptr)
			{
				*charactersWritten = length;
			}
		}
		else
		{
			// If the string is empty then we'll fill in the null-termination character by hand.
			*buffer = L'\0';

			if (charactersWritten != nullptr)
			{
				*charactersWritten = 1;
			}
		}		
	}
	catch (std::out_of_range)
	{
		hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		goto end;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedStringValue(_In_z_ const wchar_t* fieldName, _In_z_ const wchar_t* fieldValue)
{
	HRESULT hr = S_OK;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(fieldValue);

	// Write the field value
	m_jsonObject.SetValue(fieldName, fieldValue);

	// Write the meta-data
	m_fieldTypes[fieldName] = FieldTypes::String;

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::MeasureNamedCollection(_In_z_ const wchar_t* fieldName, _Out_ unsigned long* numElements) const
{
	HRESULT hr = E_FAIL;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(numElements);

	try
	{
		*numElements = m_jsonObject.GetValue(fieldName).GetCount();
	}
	catch (std::out_of_range)
	{
		hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		goto end;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollectionItem(_In_z_ const wchar_t* fieldName, _In_ unsigned long elementIndex, _Out_ RpcArchiveImpl* collectionItem)
{
	HRESULT hr = E_FAIL;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(collectionItem);

	try
	{
		*collectionItem = RpcArchiveImpl(m_jsonObject.GetValue(fieldName).GetValue(elementIndex));
	}
	catch (std::out_of_range)
	{
		if (!m_jsonObject.Exists(fieldName))
		{
			hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		}
		else
		{
			hr = E_INVALIDARG;
		}

		goto end;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollectionItem(_In_z_ const wchar_t* fieldName, _In_ unsigned long elementIndex, _Out_ RpcBinaryDataImpl* collectionItem)
{
	HRESULT hr = E_FAIL;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(collectionItem);

	try
	{
		*collectionItem = RpcBinaryDataImpl(m_jsonObject.GetValue(fieldName).GetValue(elementIndex));
	}
	catch (std::out_of_range)
	{
		if (!m_jsonObject.Exists(fieldName))
		{
			hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		}
		else
		{
			hr = E_INVALIDARG;
		}

		goto end;
	}	

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numElements) bool* collectionBuffer, _In_ unsigned long numElements) const
{
	HRESULT hr = E_FAIL;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(collectionBuffer);

	try
	{
		JsonNode collection = m_jsonObject.GetValue(fieldName);

		if(collection.GetCount() > numElements)
		{
			hr = GAMESTEST_RPC_E_DESTINATION_BUFFER_TOO_SMALL;
			goto end;
		}

		for (unsigned long index = 0; index < collection.GetCount(); ++index)
		{
			collectionBuffer[index] = collection.GetBooleanValue(index);
		}
	}
	catch (std::out_of_range)
	{
		hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		goto end;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numElements) unsigned char* collectionBuffer, _In_ unsigned long numElements) const
{
	return GetNumericCollection<unsigned char>(fieldName, collectionBuffer, numElements);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numElements) char* collectionBuffer, _In_ unsigned long numElements) const
{
	return GetNumericCollection<char>(fieldName, collectionBuffer, numElements);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numElements) signed char* collectionBuffer, _In_ unsigned long numElements) const
{
	return GetNumericCollection<signed char>(fieldName, collectionBuffer, numElements);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numElements) unsigned short* collectionBuffer, _In_ unsigned long numElements) const
{
	return GetNumericCollection<unsigned short>(fieldName, collectionBuffer, numElements);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numElements) short* collectionBuffer, _In_ unsigned long numElements) const
{
	return GetNumericCollection<short>(fieldName, collectionBuffer, numElements);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numElements) unsigned int* collectionBuffer, _In_ unsigned long numElements) const
{
	return GetNumericCollection<unsigned int>(fieldName, collectionBuffer, numElements);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numElements) int* collectionBuffer, _In_ unsigned long numElements) const
{
	return GetNumericCollection<int>(fieldName, collectionBuffer, numElements);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numElements) unsigned long* collectionBuffer, _In_ unsigned long numElements) const
{
	return GetNumericCollection<unsigned long>(fieldName, collectionBuffer, numElements);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numElements) long* collectionBuffer, _In_ unsigned long numElements) const
{
	return GetNumericCollection<long>(fieldName, collectionBuffer, numElements);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numElements) unsigned long long* collectionBuffer, _In_ unsigned long numElements) const
{
	HRESULT hr = E_FAIL;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(collectionBuffer);

	try
	{
		JsonNode collection = m_jsonObject.GetValue(fieldName);

		if(collection.GetCount() > numElements)
		{
			hr = GAMESTEST_RPC_E_DESTINATION_BUFFER_TOO_SMALL;
			goto end;
		}

		for (unsigned long index = 0; index < collection.GetCount(); ++index)
		{
			collectionBuffer[index] = _wcstoui64(collection.GetStringValue(index), nullptr, 10);
		}
	}
	catch (std::out_of_range)
	{
		hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		goto end;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numElements) long long* collectionBuffer, _In_ unsigned long numElements) const
{
	HRESULT hr = E_FAIL;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(collectionBuffer);

	try
	{
		JsonNode collection = m_jsonObject.GetValue(fieldName);

		if (collection.GetCount() > numElements)
		{
			hr = GAMESTEST_RPC_E_DESTINATION_BUFFER_TOO_SMALL;
			goto end;
		}

		for (unsigned long index = 0; index < collection.GetCount(); ++index)
		{
			collectionBuffer[index] = _wcstoi64(collection.GetStringValue(index), nullptr, 10);
		}
	}
	catch (std::out_of_range)
	{
		hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		goto end;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numElements) float* collectionBuffer, _In_ unsigned long numElements) const
{
	return GetNumericCollection<float>(fieldName, collectionBuffer, numElements);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numElements) double* collectionBuffer, _In_ unsigned long numElements) const
{
	return GetNumericCollection<double>(fieldName, collectionBuffer, numElements);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::MeasureNamedStringCollection(_In_z_ const wchar_t* fieldName, _Out_ unsigned long* numStrings, _Out_ unsigned long* totalNumStringCharacters) const
{
	HRESULT hr = E_FAIL;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(numStrings);
	VERIFY_PARAM_NOT_NULL(totalNumStringCharacters);

	try
	{
		JsonNode collection = m_jsonObject.GetValue(fieldName);

		*numStrings = collection.GetCount();
		*totalNumStringCharacters = 0;

		for (unsigned long index = 0; index < collection.GetCount(); ++index)
		{
			(*totalNumStringCharacters) += collection.GetStringValue(index).GetLength() + 1;
		}
	}
	catch (std::out_of_range)
	{
		hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		goto end;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNamedStringCollection(_In_z_ const wchar_t* fieldName, _Out_writes_(numStringPointers) wchar_t** stringPointers, _In_ unsigned long numStringPointers, _Out_writes_(numStringCharacters) wchar_t* stringContents, _In_ unsigned long numStringCharacters) const
{
	HRESULT hr = E_FAIL;	

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(stringPointers);
	VERIFY_PARAM_NOT_NULL(stringContents);
	
	unsigned long actualNumStrings;
	unsigned long actualNumCharacters;

	CHK(MeasureNamedStringCollection(fieldName, &actualNumStrings, &actualNumCharacters));

	if (actualNumStrings > numStringPointers || actualNumCharacters > numStringCharacters)
	{
		hr = GAMESTEST_RPC_E_DESTINATION_BUFFER_TOO_SMALL;
		goto end;
	}

	try
	{
		JsonNode collection = m_jsonObject.GetValue(fieldName);
		wchar_t* currentString = stringContents;

		for (unsigned long index = 0; index < collection.GetCount(); ++index)
		{
			JsonString collectionString = collection.GetStringValue(index);

			wcsncpy_s(currentString, collectionString.GetLength() + 1, collectionString, collectionString.GetLength() + 1);

			stringPointers[index] = currentString;
			currentString += collectionString.GetLength() + 1;
		}
	}
	catch (std::out_of_range)
	{
		hr = GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
		goto end;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::EnsureNamedCollectionIsEmpty(_In_z_ const wchar_t* collectionFieldName)
{
	HRESULT hr = E_FAIL;

	VERIFY_PARAM_NOT_NULL(collectionFieldName);

	m_jsonObject.GetArrayValue(collectionFieldName).Clear();

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::AddItemToNamedCollection(_In_z_ const wchar_t* collectionFieldName, RpcArchiveImpl* elementToBeAdded)
{
	HRESULT hr = E_FAIL;

	VERIFY_PARAM_NOT_NULL(collectionFieldName);
	VERIFY_PARAM_NOT_NULL(elementToBeAdded);

	// Don't add archives with no user fields
	if (!elementToBeAdded->m_fieldTypes.empty())
	{
		// Add to the field value
		JsonNode collection = m_jsonObject.GetArrayValue(collectionFieldName);
		collection.Add(elementToBeAdded->GetJson());

		// Write the meta-data
		m_fieldTypes[collectionFieldName] = FieldTypes::Archive;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::AddItemToNamedCollection(_In_z_ const wchar_t* collectionFieldName, RpcBinaryDataImpl* elementToBeAdded)
{
	HRESULT hr = E_FAIL;

	VERIFY_PARAM_NOT_NULL(collectionFieldName);
	VERIFY_PARAM_NOT_NULL(elementToBeAdded);

	if (elementToBeAdded->GetData() != nullptr)
	{
		// Add to the field value
		JsonNode collection = m_jsonObject.GetArrayValue(collectionFieldName);
		collection.Add(elementToBeAdded->GetJson());

		// Write the meta-data
		m_fieldTypes[collectionFieldName] = FieldTypes::Blob;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numElements) bool* collectionBuffer, _In_ unsigned long numElements)
{
	HRESULT hr = E_FAIL;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(collectionBuffer);

	if (numElements > 0)
	{
		JsonNode collection = m_jsonObject.GetArrayValue(fieldName);
		collection.Clear();

		for (unsigned long index = 0; index < numElements; index++)
		{
			collection.Add(collectionBuffer[index]);
		}

		// Write the meta-data
		m_fieldTypes[fieldName] = FieldTypes::Bool;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numElements) signed char* collectionBuffer, _In_ unsigned long numElements)
{
	return SetNumericCollection<signed char>(fieldName, collectionBuffer, numElements, FieldTypes::Int8);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numElements) char* collectionBuffer, _In_ unsigned long numElements)
{
	return SetNumericCollection<char>(fieldName, collectionBuffer, numElements, FieldTypes::Char);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numElements) unsigned char* collectionBuffer, _In_ unsigned long numElements)
{
	return SetNumericCollection<unsigned char>(fieldName, collectionBuffer, numElements, FieldTypes::UInt8);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numElements) short* collectionBuffer, _In_ unsigned long numElements)
{
	return SetNumericCollection<short>(fieldName, collectionBuffer, numElements, FieldTypes::Short);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numElements) unsigned short* collectionBuffer, _In_ unsigned long numElements)
{
	return SetNumericCollection<unsigned short>(fieldName, collectionBuffer, numElements, FieldTypes::UShort);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numElements) int* collectionBuffer, _In_ unsigned long numElements)
{
	return SetNumericCollection<int>(fieldName, collectionBuffer, numElements, FieldTypes::Int);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numElements) unsigned int* collectionBuffer, _In_ unsigned long numElements)
{
	return SetNumericCollection<unsigned int>(fieldName, collectionBuffer, numElements, FieldTypes::UInt);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numElements) long* collectionBuffer, _In_ unsigned long numElements)
{
	return SetNumericCollection<long>(fieldName, collectionBuffer, numElements, FieldTypes::Long);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numElements) unsigned long* collectionBuffer, _In_ unsigned long numElements)
{
	return SetNumericCollection<unsigned long>(fieldName, collectionBuffer, numElements, FieldTypes::ULong);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numElements) long long* collectionBuffer, _In_ unsigned long numElements)
{
	// http://en.wikipedia.org/wiki/JavaScript_syntax#Number
	// long long and unsigned long long values are encoded as strings because all numbers in JSON
	// are represented in binary as IEEE-754 Doubles, which provides an accuracy nearly 16 significant digits.
	// If the number becomes longer than 16 digits, then it can start to lose accuracy.
	HRESULT hr = E_FAIL;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(collectionBuffer);

	if (numElements > 0)
	{
		JsonNode collection = m_jsonObject.GetArrayValue(fieldName);
		collection.Clear();

		// The longest value of an long long is -9223372036854775808, which is 20 characters.
		// The buffer here is rounded up to the nearest power of 2.
		const int BUFFER_SIZE = 32;
		wchar_t buf[BUFFER_SIZE];

		for(unsigned long index = 0; index < numElements; index++)
		{
			_i64tow_s(collectionBuffer[index], buf, BUFFER_SIZE, 10);
			collection.Add(buf);
		}

		// Write the meta-data
		m_fieldTypes[fieldName] = FieldTypes::Int64;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numElements) unsigned long long* collectionBuffer, _In_ unsigned long numElements)
{
	// http://en.wikipedia.org/wiki/JavaScript_syntax#Number
	// long long and unsigned long long values are encoded as strings because all numbers in JSON
	// are represented in binary as IEEE-754 Doubles, which provides an accuracy nearly 16 significant digits.
	// If the number becomes longer than 16 digits, then it can start to lose accuracy.

	HRESULT hr = E_FAIL;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(collectionBuffer);

	if (numElements > 0)
	{
		JsonNode collection = m_jsonObject.GetArrayValue(fieldName);
		collection.Clear();

		// The longest value of an long long is 18446744073709551616, which is 20 characters.
		// The buffer here is rounded up to the nearest power of 2.
		const int BUFFER_SIZE = 32;
		wchar_t buf[BUFFER_SIZE];

		for(unsigned int index = 0; index < numElements; index++)
		{
			_ui64tow_s(collectionBuffer[index], buf, BUFFER_SIZE, 10);
			collection.Add(buf);
		}

		// Write the meta-data
		m_fieldTypes[fieldName] = FieldTypes::UInt64;
	}

end:
	return hr;
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numElements) float* collectionBuffer, _In_ unsigned long numElements)
{
	return SetNumericCollection<float>(fieldName, collectionBuffer, numElements, FieldTypes::Float);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numElements) double* collectionBuffer, _In_ unsigned long numElements)
{
	return SetNumericCollection<double>(fieldName, collectionBuffer, numElements, FieldTypes::Double);
}

GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::SetNamedStringCollection(_In_z_ const wchar_t* fieldName, _In_reads_(numStrings) const wchar_t* const* stringPointers, _In_ unsigned long numStrings)
{
	HRESULT hr = E_FAIL;

	VERIFY_PARAM_NOT_NULL(fieldName);
	VERIFY_PARAM_NOT_NULL(stringPointers);

	if (numStrings > 0)
	{
		// Write field value
		JsonNode collection = m_jsonObject.GetArrayValue(fieldName);
		collection.Clear();

		for(unsigned int index = 0; index < numStrings; index++)
		{
			collection.Add(stringPointers[index]);
		}

		// Write the meta-data
		m_fieldTypes[fieldName] = FieldTypes::String;
	}

end:
	return hr;
}

/// <summary>
/// Gets a type of the field specified by fieldName (useful for unit tests and external interface).
/// </summary>
/// <param name="fieldName">The name of the field.</param>
RpcArchiveImpl::FieldTypes RpcArchiveImpl::GetNamedValueType(_In_z_ const wchar_t* fieldName)
{
	FieldTypesMap::iterator it = m_fieldTypes.find(fieldName);

	if (it != m_fieldTypes.end())
	{
		return it->second;
	}
	else
	{
		return FieldTypes::Unknown;
	}
}

/// <summary>
/// Gets a value indicating whether a given field is a collection (useful for unit tests and external interface).
/// </summary>
/// <param name="fieldName">The name of the field.</param>
bool RpcArchiveImpl::IsNamedCollection(_In_z_ const wchar_t* fieldName)
{
	try
	{
		return m_jsonObject.GetValue(fieldName).GetType() == JsonNode::Array;
	}
	catch (std::out_of_range)
	{
		return false;
	}
}

void RpcArchiveImpl::Clear()
{
	if(!m_jsonObject.Empty())
	{
		// Start over with a blank underlying JSON object when archive is returned into the pool
		m_jsonObject = JsonNode();

		// Add __type field immediately to ensure it is the first one in serialized JSON, otherwise
		// The RPC client will receive "type does not support IConvertible" exception from Json contract serializer.
		AddTypeMetadataIfMissing();

		// Clear the continuously recorded meta-data, otherwise it is kept when archive is released into the pool
		m_fieldTypes.clear();
	}
}

void RpcArchiveImpl::Deserialize(const JsonNode& json)
{
	// Deep copy the specified JSON object
	m_jsonObject = json;

	// Parse the meta-data (it must have been previously serialized into the specified JSON object)
	ParseTypeMetadata();
}

RpcArchiveImpl::~RpcArchiveImpl()
{
}

void RpcArchiveImpl::AddTypeMetadataIfMissing()
{
	if (!m_jsonObject.Exists(ContainerTypeMetaKey))
	{
		m_jsonObject.SetValue(ContainerTypeMetaKey, L"RpcArchive:#Microsoft.Internal.GamesTest.Rpc.Client");
	}
}

void RpcArchiveImpl::SerializeTypeMetadata()
{
	if (!m_fieldTypes.empty())
	{
		// Get the hidden array used to store meta-data in Archive JSON structure
		JsonNode meta = m_jsonObject.GetArrayValue(FieldTypeMetaKey);
		meta.Clear();

		// Serialize all field types into the hidden array as field names followed by field types
		wchar_t fieldTypeToString[16] = {0};

		for (FieldTypesMap::iterator pos = m_fieldTypes.begin();
			pos != m_fieldTypes.end();
			++pos)
		{
			// Encode field type as a number to be stored as a string
			_itow_s((int)pos->second, fieldTypeToString, sizeof(fieldTypeToString) / sizeof(wchar_t), 10);

			// Create two consecutive array elements, one for field name and one for type
			meta.Add(pos->first.data());
			meta.Add(fieldTypeToString);
		}
	}
}

void RpcArchiveImpl::ParseTypeMetadata()
{
	// Clear current meta-data to re-create it
	m_fieldTypes.clear();

	// Get the hidden array used to store meta-data in Archive JSON structure
	if (m_jsonObject.Exists(FieldTypeMetaKey))
	{
		JsonNode meta = m_jsonObject.GetValue(FieldTypeMetaKey);

		// Validate the meta-data array
		if (meta.GetCount() % 2 > 0 || meta.GetCount() < 2)
		{
			// Ensure the size is divisible by two because fields/types are encoded in pairs.
			// Also ensure there is at least one pair to process
			return;
		}

		// Parse all field types from the hidden array where they are stored as field names followed by field types
		for (unsigned long n = 0; n < meta.GetCount(); n += 2)
		{
			// Ensure field name and type are both strings
			if (meta.GetValue(n).GetType() != JsonNode::String ||
				meta.GetValue(n + 1).GetType() != JsonNode::String)
			{
				return;
			}

			// Store the parsed meta-data;
			m_fieldTypes[(const wchar_t*)meta.GetStringValue(n)] = (FieldTypes)_wtoi(meta.GetStringValue(n + 1));
		}
	}
}

template<typename T>
GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RpcArchiveImpl::GetNumericField(_In_z_ const wchar_t* fieldName, _Out_ T* resultValue) const
{
	*resultValue = (T)0;
	
	try
	{
		*resultValue = (T)m_jsonObject.GetNumericValue(fieldName);
	}
	catch (std::out_of_range)
	{
		return GAMESTEST_RPC_E_MEMBER_NOT_FOUND_IN_ARCHIVE;
	}

	return S_OK;
}

template<typename T>
void RpcArchiveImpl::SetNumericField(_In_z_ const wchar_t* fieldName, T numericValue, FieldTypes fieldType)
{
	// Write the key and value
	m_jsonObject.SetValue(fieldName, (double)numericValue);

	// Write the meta-data
	m_fieldTypes[fieldName] = fieldType;
}